package com.york.tinyrpc.protoss.server.tcp;

import cn.hutool.core.util.IdUtil;
import com.york.tinyrpc.protoss.TinyRpcApplication;
import com.york.tinyrpc.protoss.constant.TinyRpcProtocolConstants;
import com.york.tinyrpc.protoss.enums.TinyRpcProtocolMessageSerializationEnum;
import com.york.tinyrpc.protoss.enums.TinyRpcProtocolMessageTypeEnum;
import com.york.tinyrpc.protoss.model.ServiceMetaInfo;
import com.york.tinyrpc.protoss.model.TinyRpcRequest;
import com.york.tinyrpc.protoss.model.TinyRpcResponse;
import com.york.tinyrpc.protoss.protocol.TinyRpcProtocolMessage;
import com.york.tinyrpc.protoss.server.tcp.wrapper.TcpBufferHandlerWrapper;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.NetClient;
import io.vertx.core.net.NetSocket;
import io.vertx.core.parsetools.RecordParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * 基于Vert.x实现的基于TCP连接的客户端
 */
public class VertxTcpClient {

    private static final Logger log = LoggerFactory.getLogger(VertxTcpClient.class);

    /**
     * 封装请求方法
     *
     * @param rpcRequest              请求
     * @param selectedServiceMetaInfo 被选中的服务信息
     * @return TinyRpcResponse rpc调用结果
     */
    public static TinyRpcResponse doRequest(TinyRpcRequest rpcRequest, ServiceMetaInfo selectedServiceMetaInfo) {
        // 建立TCP连接
        NetClient netClient = Vertx.vertx().createNetClient();
        // responseFuture是异步处理结果
        CompletableFuture<TinyRpcResponse> responseFuture = new CompletableFuture<>();
        netClient.connect(
                selectedServiceMetaInfo.getServicePort(),
                selectedServiceMetaInfo.getServiceHost(),
                result -> {
                    if (result.succeeded()) {
                        log.info("连接到TCP服务");
                        NetSocket socket = result.result();

                        // 根据配置获取序列化方式
                        byte serializationId = (byte) TinyRpcProtocolMessageSerializationEnum
                                .getEnumByName(TinyRpcApplication.getRpcConfig().getSerializer())
                                .getValue();

                        // 通过socket发送请求，构建请求头header，这里的请求体bodyLength是通过编码时设置的
                        TinyRpcProtocolMessage.TinyRpcProtocolMessageHeader header = TinyRpcProtocolMessage.TinyRpcProtocolMessageHeader.builder()
                                .magic(TinyRpcProtocolConstants.PROTOCOL_MAGIC)
                                .version(TinyRpcProtocolConstants.PROTOCOL_VERSION)
                                .serializationId(serializationId)
                                .type((byte) TinyRpcProtocolMessageTypeEnum.REQUEST.getType())
                                // 生成全局请求ID
                                .requestId(IdUtil.getSnowflakeNextId())
                                .build();

                        // 构建请求消息结构
                        TinyRpcProtocolMessage<TinyRpcRequest> protocolMessage =
                                TinyRpcProtocolMessage.<TinyRpcRequest>builder()
                                        .header(header)
                                        .body(rpcRequest)
                                        .build();

                        // 发送消息
                        try {
                            Buffer encode = TinyRpcProtocolMessage.encode(protocolMessage);
                            socket.write(encode);
                        } catch (Exception e) {
                            log.error("协议消息编码错误");
                        }

                        // 异步获取结果，同理使用增强版Handle<Buffer>
                        TcpBufferHandlerWrapper bufferHandlerWrapper = new TcpBufferHandlerWrapper(
                                buffer -> {
                                    try {
                                        TinyRpcProtocolMessage<TinyRpcResponse> responseProtocolMessage =
                                                (TinyRpcProtocolMessage<TinyRpcResponse>) TinyRpcProtocolMessage.decode(buffer);
                                        responseFuture.complete(responseProtocolMessage.getBody());
                                    } catch (IOException e) {
                                        throw new RuntimeException("协议消息解码错误");
                                    }
                                });
                        socket.handler(bufferHandlerWrapper);
                    } else {
                        log.error("未能连接到TCP服务");
                    }
                }
        );
        TinyRpcResponse rpcResponse = null;
        try {
            rpcResponse = responseFuture.get();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }

        // 最后关闭连接
        netClient.close();
        return rpcResponse;
    }
}
